/**
 * 
 */
package dms.repository.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.Transaction;

import dms.model.Building;
import dms.model.Dormitory;
import dms.model.form.Mail;
import dms.model.util.FindUtil;
import dms.model.util.PageIterator;
import dms.repository.MailRepository;
import dms.util.HibernateUtil;

/**
 * @author 李健
 *
 */
public class MailRepositoryHibernate implements MailRepository {

	private static final Log log = LogFactory.getLog(MailRepositoryHibernate.class);
	
	@Override
	public Mail createMail(Mail mail) throws Exception{
		Session session = null;
		Transaction tx = null;
		
		try {
			session = HibernateUtil.getSessionFactory().getCurrentSession();
			tx = session.beginTransaction();
			
			session.save(mail);
		
		} catch (Exception e) {
			log.error(e.getMessage());
			throw e;
		} 
		return mail;
	}

	@Override
	public void deleteById(Long id) throws Exception {
		Session session = null;
		Transaction tx = null;
		
		String deleteQuery = "delete from Mail m where m.id = :id";
		
		try {
			session = HibernateUtil.getSessionFactory().getCurrentSession();
			tx = session.beginTransaction();
					
			session.createQuery(deleteQuery)
				   .setParameter("id", id)
				   .executeUpdate();
		
		} catch (Exception e) {
			log.error(e.getMessage());
			throw e;
		} 
	}

	@Override
	public PageIterator<Mail> findByBuilding(int start, int count, Building building, Date fromDate,
			Date toDate) {
		Map keyValue = new HashMap();
		keyValue.put("dormitory.building", building);
		return find(start, count, keyValue, fromDate, toDate);
	}

	@Override
	public Mail findById(Long id) {
		Session session = null;
		Transaction tx = null;
		
		Mail mail = null;
		
		try {
			session = HibernateUtil.getSessionFactory().getCurrentSession();
			tx = session.beginTransaction();
			
			mail = (Mail) session.load(Mail.class, id);
			//initialize property
			Hibernate.initialize(mail);
			
		} catch (Exception e) {
			log.error(e.getMessage());
		}
		return mail;
	}

	@Override
	public PageIterator<Mail> findByStudent(int start, int count, String student, Date fromDate,
			Date toDate) {
		Map keyValue = new HashMap();
		keyValue.put("student", student);
		return find(start, count, keyValue, fromDate, toDate);
	}

	private PageIterator<Mail> find(int start, int count, Map keyValue, Date fromDate, Date toDate) {
		Session session = null;
		Transaction tx = null;
		List<Mail> list = null;
		PageIterator<Mail> iter = null;
		Long allCount = null;		
		
		Object[] queriesAndParams = this.makeQueriesAndParams(start, count, keyValue, fromDate, toDate);
		String countQuery = (String) queriesAndParams[0];
		String findQuery = (String) queriesAndParams[1];
		Collection params = (Collection) queriesAndParams[2];
				
		session = HibernateUtil.getSessionFactory().getCurrentSession();
		tx = session.beginTransaction();
		
		allCount = FindUtil.calculateAllCount(session, countQuery, params);
		list = FindUtil.getEntitiesByPage(session, findQuery, params, start, count);
		iter = new PageIterator<Mail>(list, allCount, start, count);						
		return iter;
	}
	
	
	@Override
	public Mail updateMail(Mail mailInfo) throws Exception {
		Session session = null;
		Transaction tx = null;
		
		Mail mail = null;
		
		try {
			session = HibernateUtil.getSessionFactory().getCurrentSession();
			tx = session.beginTransaction();
			
			mail = (Mail) session.merge(mailInfo);
			
		} catch (Exception e) {
			log.error(e.getMessage());	
			throw e;
		} 
		return mail;
	}

	@Override
	public PageIterator<Mail> findByDormitory(int start, int count,
			Dormitory dormitory, Date fromDate, Date toDate) {
		Map keyValue = new HashMap();
		keyValue.put("dormitory", dormitory);
		
		return this.find(start, count, keyValue, fromDate, toDate);
	}

	@Override
	public void deleteMails(List<Long> ids) throws Exception {
		Session session = null;
		Transaction tx = null;
		
		String deleteQuery = " delete from Mail m where m.id in (:ids) ";
		try {
			session = HibernateUtil.getSessionFactory().getCurrentSession();
			tx = session.beginTransaction();
			
			session.createQuery(deleteQuery)
				   .setParameterList("ids", ids)
				   .executeUpdate();
			
			
		} catch (Exception e) {
			log.error(e.getMessage());
			throw e;
		}
	}

	@Override
	public PageIterator<Mail> findByBuildingStudent(int start, int count,
			Building building, String student, Date fromDate, Date toDate) {
		Map keyValue = new HashMap();
		keyValue.put("dormitory.building", building);
		keyValue.put("student", student);
		return find(start, count, keyValue, fromDate, toDate);
	}

	private Object[] makeQueriesAndParams(int start, int count, Map keyValue, Date fromDate, Date toDate) {
		
		String countQuery = "select count(*) from Mail m  ";
		String findQuery = "select m from Mail m  ";
		StringBuilder conditionQuery = new StringBuilder("");
		
		if (!keyValue.isEmpty()) {
			conditionQuery.append(" where ") ;
		}
		
		Set set = keyValue.entrySet();
		Iterator keyIter = set.iterator();
		
		Collection params = new ArrayList();
				
		while (keyIter.hasNext()) {
			Map.Entry<String, Object> entry = (Entry<String, Object>) keyIter.next();
			String name = entry.getKey();
			Object value = entry.getValue();
			
			conditionQuery.append(" m." + name + " = ? and ") ;
			params.add(value);
		}
		
		if (conditionQuery.indexOf("where") == -1) {
			conditionQuery.append(" where ");
		}
		if (fromDate != null && toDate == null) {
			conditionQuery.append("  m.arriveDate >= ?  and ");
			params.add(fromDate);
		}
		if (fromDate == null && toDate != null) {
			conditionQuery.append("  m.arriveDate <= ?  and ");
			params.add(toDate);
		}
		if (fromDate != null && toDate != null) {
			conditionQuery.append("  (m.arriveDate between ? and ?)  and ");
			params.add(fromDate);
			params.add(toDate);
		}

		
		conditionQuery.append(" m.id = m.id ");
		countQuery += conditionQuery;
		findQuery += conditionQuery;
		
		return new Object[]{countQuery, findQuery, params};
	}
}
